package smtp;
import java.io.*;

import java.net.Socket;
import java.net.InetAddress;            //Is this allowed????
import java.net.UnknownHostException;   //IS this allowed????
import java.util.*;

import dns.DnsResolver;



public class SmtpSender {

	private class SmtpException extends Exception {
		
		public SmtpException(String msg) {
			super(msg);
		}
	}	
	
    // SMTP port is defined
    private static final int PORT = 25;
    private static final String hostname = "ik2213.lab";
    public static final String SENDER = "noreply@" + hostname; 
    
    private Map<Integer, String> codes; 
    private QuotedEncoder encoder;
    private SubjectEncoder subjectencoder;
    private DnsResolver resolver;
    
    public SmtpSender() {    	
    	createCodesMap();
    	encoder = new QuotedEncoder();
    	subjectencoder = new SubjectEncoder();
    	resolver = new DnsResolver();
    }

    public static void main(String[] args) {
    	SmtpSender ss = new SmtpSender();
    	try {
			ss.sendMail("visockas@kth.se", "visockas@vilius.org", "Hello" , "Some content");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
        
    
    /* 
     * Errors Codes according to
     * http://www.faqs.org/rfcs/rfc2821.html
     *  
     * */
    private void createCodesMap() {
    	codes = new HashMap<Integer, String>();
    	codes.put(211, "System status, or system help reply");
    	codes.put(214, "Help message");
    	codes.put(220, "<domain> Service ready");
    	codes.put(221, "<domain> Service closing transmission channel");
    	codes.put(250, "Requested mail action okay, completed");
    	codes.put(251, "User not local; will forward to <forward-path>");
    	codes.put(252, "Cannot VRFY user, but will accept message and attempt delivery");    	
    	codes.put(354, "Start mail input; end with <CRLF>.<CRLF>");
    	codes.put(421, "<domain> Service not available, closing transmission channel");
    	codes.put(450, "Requested mail action not taken: mailbox unavailable");
    	codes.put(451, "Requested action aborted: local error in processing");
    	codes.put(452, "Requested action not taken: insufficient system storage");
    	codes.put(500, "Syntax error, command unrecognized");
    	codes.put(501, "Syntax error in parameters or arguments");
    	codes.put(502, "Command not implemented");
    	codes.put(503, "Bad sequence of commands");
    	codes.put(504, "Command parameter not implemented");
    	codes.put(550, "Requested action not taken: mailbox unavailable");
    	codes.put(551, "User not local; please try <forward-path>");
    	codes.put(552, "Requested mail action aborted: exceeded storage allocation");
    	codes.put(553, "Requested action not taken: mailbox name not allowed");
    	codes.put(554, "Transaction failed.");    	
    }
    
    /* Parse response and assert whether reply code was such as excpected, else throw exc */
    private void assertReply(String reply, int expected) throws SmtpException{
		String[] rez = reply.split(" ", 2);
		int replyCode = Integer.parseInt(rez[0]);
		String replyMsg = rez[1];
		
		if (replyCode != expected) {
			String errorMessage = codes.get(replyCode);
			String fullMesssage = errorMessage + ": " + replyMsg;
			throw new SmtpException(fullMesssage);
		}
    }
    
    
    // Format MIME like body of message
    private String getBody(String mailfrom, String rcptto, String subject, String content) {
    	String body = "";
    	body += "From: \"Sender\" <" + mailfrom + ">\n";
    	body += "To: \"Receiver\" <" + rcptto + ">\n";  
    	body += "Subject: " + subjectencoder.encode(subject) + "\n";
    	
    	// Mime headers
    	body += "MIME-Version:1.1\n";
    	body += "Content-Type:text/plain\n";
    	body += "Content-Id:123@ik2213.lab\n";
    	body += "Content-Transfer-Encoding:quoted-printable\n";
    	body += "Content-Description:email\n";
    	    	    	
    	body += encoder.encode(content);    	
    	return body;
    }
        
    /* Sends email message after connecting via sockets with server */
    public void sendMail(String mailfrom, String rcptto, String subject, String content) throws Exception {
   
    	Socket s = null;
        try {
        	InetAddress host = resolver.getServerIP(rcptto);
            s = new Socket(host, PORT);

            Scanner input = new Scanner(s.getInputStream());
            PrintWriter output = new PrintWriter(s.getOutputStream(),true);

            String response;
            
            // Wait for greeting
            response = input.nextLine();            
            assertReply(response, 220);
            
            
            // Send hello message
            output.println("HELO " + hostname);
            response = input.nextLine();            
            assertReply(response, 250);
            
            // Send MAIL FROM message
            output.println("MAIL FROM:<" + mailfrom + ">");
            response = input.nextLine();
            assertReply(response, 250);
            
            // Send TCPT TO message
            output.println("RCPT TO:<" + rcptto + ">");
            response = input.nextLine();
            assertReply(response, 250);            
            
            // Start Sending data
            output.println("DATA");    
            response = input.nextLine();
            assertReply(response, 354);               
            
            // Sending actuall message
            String emailBody = getBody(mailfrom, rcptto, subject, content);
            output.println(emailBody);
            output.println(".");
            response = input.nextLine();
            assertReply(response, 250);    
            
            // Say goodbye
            output.println("QUIT");
            response = input.nextLine();
            // assertReply(response, 221);                            

        }

        finally {
            try {
                s.close();
            }
            catch (IOException ioe){
            }
        }

    }

}
